package com.bobo.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bobo.base.Q;
import com.bobo.base.mybatis.ServiceImpl;
import com.bobo.base.quartz.QuartzConstants;
import com.bobo.base.quartz.QuartzJobScheduler;
import com.bobo.mapper.SysJobMapper;
import com.bobo.pojo.SysJob;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;


/**
 * 定时任务
 *
 * @author LILIBO
 * @since 2023-02-18
 */
@Slf4j
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {

    @Resource
    private SysJobMapper sysJobMapper;

    @Resource
    private QuartzJobScheduler quartzJobScheduler;

    /**
     * 获取定时任务分页数据
     *
     * @param page
     * @param param
     * @return
     */
    @Override
    public IPage<SysJob> getPageList(Page page, Q param) {
        return sysJobMapper.getPageList(page, param);
    }

    /**
     * 项目启动时初始化定时器，主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void initJob() {
        // 清空原有任务
        quartzJobScheduler.clear();
        // 重新加载定时任务
        List<SysJob> jobList = sysJobMapper.selectList(null);
        log.info("~~~~~~ Quartz initialize schedule Jobs: size={}", jobList.size());
        for (SysJob job : jobList) {
            quartzJobScheduler.scheduleJob(job);
        }
    }

    /**
     * 添加简单的定时任务（在时间范围内每隔n秒执行一次）
     *
     * @param sysJob 定时任务
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param interval 时间间隔
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean createSimpleJob(SysJob sysJob, Date startTime, Date endTime, int interval) {
        if (1 == sysJobMapper.insert(sysJob)) {
            return quartzJobScheduler.createSimpleJob(sysJob, startTime, endTime, interval);
        }
        return false;
    }

    /**
     * 添加定时任务（通过Cron表达式定义周期）
     *
     * @param sysJob 定时任务
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean addJob(SysJob sysJob) {
        if (1 == sysJobMapper.insert(sysJob)) {
            return quartzJobScheduler.scheduleJob(sysJob);
        }
        return false;
    }

    /**
     * 修改定时任务（通过Cron表达式重新定义周期）
     *
     * @param param 定时任务
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean modifyJob(SysJob param) {
        QueryWrapper<SysJob> wrapper = new QueryWrapper<>();
        wrapper.eq("job_name", param.getJobName());
        wrapper.eq("job_group", param.getJobGroup());
        SysJob sysJob = sysJobMapper.selectOne(wrapper);
        if (sysJob == null) {
            return false;
        }
        sysJob.setCronExpression(param.getCronExpression());
        sysJob.setStatus(param.getStatus());
        if (1 == sysJobMapper.updateById(sysJob)) {
            // 重新安排定时任务（通过Cron表达式重新定义周期）
            return quartzJobScheduler.rescheduleJob(sysJob);
        }
        return false;
    }

    /**
     * 暂停定时任务（暂停已存在的任务）
     *
     * @param param 定时任务
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean pauseJob(SysJob param) {
        QueryWrapper<SysJob> wrapper = new QueryWrapper<>();
        wrapper.eq("job_name", param.getJobName());
        wrapper.eq("job_group", param.getJobGroup());
        SysJob sysJob = sysJobMapper.selectOne(wrapper);
        if (sysJob == null) {
            return false;
        }
        // 设置数据库状态为暂停
        sysJob.setStatus(QuartzConstants.Status.PAUSE.getValue());
        if (1 == sysJobMapper.updateById(sysJob)) {
            // 暂停定时任务（暂停已存在的任务）
            return quartzJobScheduler.pauseJob(sysJob);
        }
        return false;
    }

    /**
     * 启动定时任务（从暂停状态恢复任务运行）
     *
     * @param param 定时任务
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean resumeJob(SysJob param) {
        QueryWrapper<SysJob> wrapper = new QueryWrapper<>();
        wrapper.eq("job_name", param.getJobName());
        wrapper.eq("job_group", param.getJobGroup());
        SysJob sysJob = sysJobMapper.selectOne(wrapper);
        if (sysJob == null) {
            return false;
        }
        sysJob.setStatus(QuartzConstants.Status.NORMAL.getValue());
        if (1 == sysJobMapper.updateById(sysJob)) {
            // 恢复定时任务（从暂停状态恢复任务运行）
            return quartzJobScheduler.resumeJob(sysJob);
        }
        return false;
    }

    /**
     * 删除定时任务（任务将从系统移除）
     *
     * @param jobId 定时任务ID
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean deleteJob(Long jobId) {
        SysJob sysJob = sysJobMapper.selectById(jobId);
        if (sysJob == null) {
            return false;
        }
        if (1 == sysJobMapper.deleteById(jobId)) {
            // 删除定时任务（任务将从系统移除）
            return quartzJobScheduler.deleteJob(sysJob);
        }
        return false;
    }

    /**
     * 批量删除定时任务（任务将从系统移除）
     *
     * @param ids 定时任务ID集合
     * @return 是否成功
     */
    @Override
    @Transactional // 声明式事务
    public boolean removeJobs(List<Long> ids) {
        // 备份数据库中对应的任务
        List<SysJob> sysJobs = sysJobMapper.selectBatchIds(ids); // 1 2 3 4 5 8
        try {
            for (SysJob sysJob : sysJobs) {
                if (sysJob != null) {
                    // 删除定时任务（任务将从系统移除）
                    if (quartzJobScheduler.deleteJob(sysJob)) {
                        // 删除数据库中的记录
                        sysJobMapper.deleteById(sysJob.getId());
                    }
                }
            }
        } catch (Exception e) {
            // 让任务与数据库中的记录保持最终一致（无法借助数据库事务回滚回退已删除的任务）
            for (SysJob sysJob : sysJobs) {
                // 把任务加回去（需要测试，是否会覆盖）
                return quartzJobScheduler.scheduleJob(sysJob);
            }
            throw e; // 有问题抛出异常，让数据库回滚
        }
        return true;
    }

}
